﻿namespace Hainet

open SdlDotNet.Core
open SdlDotNet.Graphics.Sprites
open SdlDotNet.Input
open System
open System.Reflection
open System.Collections.Generic

module Object =

    [<AbstractClass>]
    type Object() =
        abstract Step: unit -> unit
            
    type Active(sprite: Sprite) =
        inherit Object()
        
        ///Graphics
        let mutable visible: bool = true
            
        ///Physics
        let mutable position = new Position()
        let mutable speed = new Speed()
        let mutable acceleration = new Acceleration()
        
        member this.Visible
            with get () = visible
            and set vis = visible <- vis
        
        member this.Sprite = sprite
        
        member this.Position
            with get () = position
            and set pos = position <- pos
        member this.Speed
            with get () = speed
            and set spd = speed <- spd  
        member this.Acceleration = acceleration
        
        override this.Step() =
            this.Position <- this.Position + 1.0<Step> * this.Speed
            this.Speed <- this.Speed + 1.0<Step> * this.Acceleration
        
        abstract OnIntersectBoundary: Boundary -> unit
        default this.OnIntersectBoundary _ = ()
        
        member this.MotionAdd(vec) =
            this.Speed <- this.Speed + vec
    
    ///Key event hattributes
    [<AttributeUsage(AttributeTargets.Method)>]
    type OnKeyPress(key: Key) =
        inherit Attribute()
        member this.Key = key
        
    [<AttributeUsage(AttributeTargets.Method)>]
    type OnKey(key: Key) =
        inherit Attribute()
        member this.Key = key
        
    [<AttributeUsage(AttributeTargets.Method)>]
    type OnKeyRelease(key: Key) =
        inherit Attribute()
        member this.Key = key            
            
    type KeyHandler = unit -> unit      
            
    type Controllable =
        inherit Active
   
        val mutable keyPressHandlers: Dictionary<Key, KeyHandler>
        val mutable keyReleaseHandlers: Dictionary<Key, KeyHandler>
        val mutable keyHandlers: Dictionary<Key, KeyHandler>
        val mutable pressedKeys: HashSet<Key>
        
        member this.KeyPress sender (args: KeyboardEventArgs) =
            if this.keyHandlers.ContainsKey(args.Key) then
                ignore <| this.pressedKeys.Add(args.Key)
            if (this.keyPressHandlers.ContainsKey(args.Key)) then
                this.keyPressHandlers.[args.Key]()
                
        member this.KeyRelease sender (args: KeyboardEventArgs) =
            ignore <| this.pressedKeys.Remove(args.Key)
            if (this.keyReleaseHandlers.ContainsKey(args.Key)) then
                this.keyReleaseHandlers.[args.Key]()
            
        override this.Step() =
            for key in this.pressedKeys do
                this.keyHandlers.[key]()
            base.Step()
        
        new (sprite) as this =
            {
                inherit Active(sprite)
                keyPressHandlers = new Dictionary<Key, KeyHandler>()
                keyHandlers = new Dictionary<Key, KeyHandler>()
                keyReleaseHandlers = new Dictionary<Key, KeyHandler>()
                pressedKeys = new HashSet<Key>()
            }
            then
                let t = this.GetType()
                
                let (|HasType|) x = x.GetType()
                
                t.GetMethods(BindingFlags.Public ||| BindingFlags.Instance ||| BindingFlags.DeclaredOnly)
                |> Seq.map(fun mi -> mi, mi.GetCustomAttributes(false))
                |> Seq.iter(fun (methodInfo, methodAttributes) ->
                    methodAttributes |> 
                    Array.iter (fun attr ->
                        match attr with
                        | a when attr.GetType() = typeof<OnKeyPress> ->
                            this.keyPressHandlers.[(attr :?> OnKeyPress).Key] <- (fun () -> ignore <| methodInfo.Invoke(this, [||]))
                        | a when attr.GetType() = typeof<OnKey> ->
                            this.keyHandlers.[(attr :?> OnKey).Key] <- (fun () -> ignore <| methodInfo.Invoke(this, [||]))
                        | a when attr.GetType() = typeof<OnKeyRelease> ->
                            this.keyHandlers.[(attr :?> OnKeyRelease).Key] <- (fun () -> ignore <| methodInfo.Invoke(this, [||]))
                        | _ -> ()
                        )
                    )
                    
                Events.KeyboardDown.AddHandler(new EventHandler<KeyboardEventArgs>(this.KeyPress))
                Events.KeyboardUp.AddHandler(new EventHandler<KeyboardEventArgs>(this.KeyRelease))
